package edu.hcmus.sow.web.user;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.validation.Valid;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.acls.domain.BasePermission;
import org.springframework.security.acls.model.Permission;
import org.springframework.security.acls.model.Sid;
import org.springframework.stereotype.Controller;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.ModelAndView;

import edu.hcmus.sow.dao.AuthoritiesDAO;
import edu.hcmus.sow.dao.UserDAO;
import edu.hcmus.sow.dao.UserProfileDAO;
import edu.hcmus.sow.domain.Folder;
import edu.hcmus.sow.domain.User;
import edu.hcmus.sow.domain.UserProfile;
import edu.hcmus.sow.domain.security.CustomPrincipalSid;
import edu.hcmus.sow.service.DocumentService;
import edu.hcmus.sow.service.SecurityService;
import edu.hcmus.sow.service.UserService;

@Controller
public class UserDetailController {
   @Autowired
   private UserService userService;

   @Autowired
   private DocumentService documentService;

   @Autowired
   private SecurityService securityService;

   @Autowired
   private UserDAO userDAO;

   @Autowired
   private AuthoritiesDAO authoritiesDAO;

   @Autowired
   private UserProfileDAO userProfileDAO;

   @RequestMapping("/admin/user/{id}")
   public ModelAndView display(@PathVariable("id") int id) {
      List<Map<String, Object>> repositories = new ArrayList<Map<String, Object>>();
      Sid sid = new CustomPrincipalSid(String.valueOf(id));

      for (Folder repository : documentService.getRepositories()) {
         Set<Permission> permissions = securityService.getPermissions(repository.getClass(), repository.getId(), sid);
         Map<String, Object> map = new HashMap<String, Object>();
         map.put("id", repository.getId());
         map.put("name", repository.getName());
         map.put("read", permissions.contains(BasePermission.READ));
         map.put("write", permissions.contains(BasePermission.WRITE));
         map.put("admin", permissions.contains(BasePermission.ADMINISTRATION));

         repositories.add(map);
      }

      UserProfile profile = userProfileDAO.getByUserID(id);
      UserProfileForm profileForm = new UserProfileForm();
      profileForm.setId(id);
      profileForm.setUsername(profile.getUserByUserId().getUsername());
      profileForm.setEnabled(profile.getUserByUserId().getEnabled());
      profileForm.setFirstName(profile.getFirstName());
      profileForm.setLastName(profile.getLastName());
      profileForm.setMiddleName(profile.getMiddleName());
      profileForm.setEmail(profile.getEmail());
      profileForm.setPhone(profile.getPhone());
      profileForm.setRole(userService.getRole(id).getId());

      ModelAndView mav = new ModelAndView();
      mav.addObject("id", id);
      mav.addObject("repositories", repositories);
      mav.addObject("profileForm", profileForm);
      mav.addObject("roles", authoritiesDAO.findAll());
      mav.setViewName("admin/user_detail");
      return mav;
   }

   @RequestMapping(value = "/admin/user/{userId}/permissions/update", method = RequestMethod.POST)
   @ResponseBody
   public void updateUserPermissions(@PathVariable("userId") int userId,
         @RequestParam(value = "repositoryIds", required = false) int[] repositoryIds,
         @RequestParam(value = "reads", required = false) int[] reads,
         @RequestParam(value = "writes", required = false) int[] writes,
         @RequestParam(value = "admins", required = false) int[] admins) {

      Set<Integer> readSet = toSet(reads);
      Set<Integer> writeSet = toSet(writes);
      Set<Integer> adminSet = toSet(admins);
      Sid sid = new CustomPrincipalSid(String.valueOf(userId));

      for (int repositoryId : repositoryIds) {
         Set<Permission> permissions = new HashSet<Permission>();

         if (readSet.contains(repositoryId)) {
            permissions.add(BasePermission.READ);
         }

         if (writeSet.contains(repositoryId)) {
            permissions.add(BasePermission.WRITE);
         }
         if (adminSet.contains(repositoryId)) {
            permissions.add(BasePermission.ADMINISTRATION);
         }

         securityService.grantPermissions(Folder.class, repositoryId, sid, permissions);
      }
   }

   private Set<Integer> toSet(int[] array) {
      Set<Integer> set = new HashSet<Integer>();
      if (array != null) {
         for (int value : array) {
            set.add(value);
         }
      }
      return set;
   }

   @RequestMapping(value = "/ajax/admin/user/profile/update", method = RequestMethod.POST)
   public ModelAndView updateUserProfile(@ModelAttribute("profileForm") @Valid UserProfileForm profileForm,
         BindingResult result) {
      profileForm.validate(result, userDAO);

      if (!result.hasErrors()) {
         User user = userService.saveUser(profileForm.getId(), profileForm.getUsername(), profileForm.getPassword(),
               profileForm.getEnabled());
         userService.saveUserProfile(user, profileForm.getFirstName(), profileForm.getLastName(),
               profileForm.getMiddleName(), profileForm.getEmail(), profileForm.getPhone());
         userService.updateUserRole(user, profileForm.getRole());
      }

      ModelAndView mav = new ModelAndView();
      mav.addObject("profileForm", profileForm);
      mav.addObject("roles", authoritiesDAO.findAll());
      mav.addObject("hasErrors", result.hasErrors());
      mav.setViewName("controls/user_profile_form");
      return mav;
   }
}
